package com.code2roc.dbm.framework.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.xa.DruidXADataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.code2roc.dbm.framework.util.ConvertOp;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.jta.JtaTransactionManager;

import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.*;

@Configuration
public class DataSourceConfig {
    private Logger logger = LoggerFactory.getLogger(DataSourceConfig.class);
    @Autowired
    private Environment env;
    @Autowired
    private ApplicationContext applicationContext;
    private Binder binder;
    @Value("${datasource.master.driverClassName}")
    private String driverClassName;
    @Value("${datasource.master.url}")
    private String dbUrl;
    @Value("${datasource.master.username}")
    private String username;
    @Value("${datasource.master.password}")
    private String password;

    //动态数据源初始化
    @Bean(name = "dynamicDataSource")
    @Qualifier("dynamicDataSource")
    public DynamicDataSource dynamicDataSource() throws SQLException {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDebug(false);
        //配置缺省的数据源
        // 默认数据源配置 DefaultTargetDataSource
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        //额外数据源配置 TargetDataSources
        targetDataSources.put("master", masterDataSource());
        dynamicDataSource.setTargetDataSources(targetDataSources);

        //读取配置文件，创建配置的从数据库
        try{
            Map config, defauleDataSourceProperties;
            binder = Binder.get(env);
            List<Map> configs = binder.bind("datasource.cluster", Bindable.listOf(Map.class)).get();
            for (int i = 0; i < configs.size(); i++) {
                config = configs.get(i);
                String key = ConvertOp.convert2String(config.get("key"));
                String type = ConvertOp.convert2String(config.get("type"));
                String driverClassName = ConvertOp.convert2String(config.get("driverClassName"));
                String url = ConvertOp.convert2String(config.get("url"));
                String username = ConvertOp.convert2String(config.get("username"));
                String password = ConvertOp.convert2String(config.get("password"));
                dynamicDataSource.createDataSource(key,driverClassName,url,username,password,type);
                logger.info("注册数据源{}成功", key);
            }
        }catch (NoSuchElementException e){

        }catch (Exception e){
            throw e;
        }
        //提供业务代码注册接口注册非配置数据源
        List<DataSourceModel> dataSourceModelList = getExtraDataSource();
        for (DataSourceModel dataSourceModel: dataSourceModelList) {
            String key = ConvertOp.convert2String(dataSourceModel.getDatasourceId());
            String type = ConvertOp.convert2String(dataSourceModel.getDatabasetype());
            String driverClassName = ConvertOp.convert2String(dataSourceModel.getDriveClassName());
            String url = ConvertOp.convert2String(dataSourceModel.getUrl());
            String username = ConvertOp.convert2String(dataSourceModel.getUserName());
            String password = ConvertOp.convert2String(dataSourceModel.getPassWord());
            dynamicDataSource.createDataSource(key,driverClassName,url,username,password,type);
            logger.info("注册数据源{}成功", key);
        }
        return dynamicDataSource;
    }

    //主数据源
    @Bean
    @Primary
    @Qualifier("masterDataSource")
    public DataSource masterDataSource() {
        DruidDataSource datasource = new DruidDataSource();
        if(driverClassName.equals("com.mysql.cj.jdbc.Driver")){
            if(!dbUrl.contains("useOldAliasMetadataBehavior")){
                dbUrl += "&useOldAliasMetadataBehavior=true";
            }
            if(!dbUrl.contains("useAffectedRows")){
                dbUrl += "&useAffectedRows=true";
            }
        }
        if(driverClassName.equals("org.sqlite.JDBC")){
            initSqliteFile(dbUrl.replace("jdbc:sqlite:",""));
        }
        datasource.setUrl(this.dbUrl);
        datasource.setUsername(username);
        datasource.setPassword(password);
        datasource.setDriverClassName(driverClassName);
        //configuration
        datasource.setInitialSize(1);
        datasource.setMinIdle(3);
        datasource.setMaxActive(20);
        datasource.setMaxWait(60000);
        datasource.setTimeBetweenEvictionRunsMillis(60000);
        datasource.setMinEvictableIdleTimeMillis(60000);
        datasource.setValidationQuery("select 'x'");
        datasource.setTestWhileIdle(true);
        datasource.setTestOnBorrow(false);
        datasource.setTestOnReturn(false);
        datasource.setMaxPoolPreparedStatementPerConnectionSize(20);
        datasource.setLogAbandoned(false); //移除泄露连接发生是是否记录日志
        if(driverClassName.equals("org.sqlite.JDBC")){
            datasource.setPoolPreparedStatements(false);
        }else{
            datasource.setPoolPreparedStatements(true);
        }
        try {
            if(!driverClassName.equals("org.sqlite.JDBC")){
                datasource.setFilters("stat,slf4j");
            }
        } catch (SQLException e) {
            logger.error("druid configuration initialization filter", e);
        }
        datasource.setConnectionProperties("druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000");//connectionProperties);
        return datasource;
    }

    public static void initSqliteFile(String filePath){
        File file = new File(filePath);

        File dir = file.getParentFile();
        if(!dir.exists()){
            dir.mkdirs();
        }

        if(!file.exists()){
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //主数据源事务管理器
    @Primary
    @Bean("masterTransactionManager")
    public DataSourceTransactionManager MasterTransactionManager() {
        return new DataSourceTransactionManager(masterDataSource());
    }


    //获取通过代码注入的从库数据源
    private List<DataSourceModel> getExtraDataSource(){
        List<DataSourceModel> dataSourceModelList = new ArrayList<>();
        Map<String, IExtraDataSourceRegist> res = applicationContext.getBeansOfType(IExtraDataSourceRegist.class);
        for (Map.Entry en :res.entrySet()) {
            IExtraDataSourceRegist service = (IExtraDataSourceRegist)en.getValue();
            dataSourceModelList.addAll(service.getExtraDataSourc());
        }
        return dataSourceModelList;
    }

    //druid监控声明设置用户名密码
    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean reg = new ServletRegistrationBean();
        reg.setServlet(new StatViewServlet());
        reg.addUrlMappings("/druid/*");
        reg.addInitParameter("allow", ""); //白名单
        reg.addInitParameter("loginUsername", "admin123");
        reg.addInitParameter("loginPassword", "abcd@1234");
        return reg;
    }

    //mybaits配置数据源
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        final PackagesSqlSessionFactoryBean sessionFactory = new PackagesSqlSessionFactoryBean();
        sessionFactory.setDataSource(dynamicDataSource());
        sessionFactory.setTransactionFactory(new DynamicTransactionFactory());
        //关闭驼峰转换，防止带下划线的字段无法映射
        sessionFactory.getObject().getConfiguration().setMapUnderscoreToCamelCase(false);
        MybatisConfiguration mybatisConfiguration =  sessionFactory.getConfiguration();
        if(mybatisConfiguration==null){
            mybatisConfiguration = new MybatisConfiguration();
        }
        sessionFactory.setConfiguration(mybatisConfiguration);
        return sessionFactory.getObject();
    }

    //jdbctemplate配置数据源
    @Bean
    public DynamicJdbcTemplate jdbcTemplate(){
        DynamicJdbcTemplate jdbcTemplate = null;
        try{
            jdbcTemplate = new DynamicJdbcTemplate(dynamicDataSource());
        }catch (Exception e){
            e.printStackTrace();
        }
        return jdbcTemplate;
    }
}
